package community.hospital.service.application.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import community.hospital.common.*;
import community.hospital.convert.RoleConvert;
import community.hospital.convert.UserConvert;
import community.hospital.entity.Menu;
import community.hospital.entity.Role;
import community.hospital.entity.User;
import community.hospital.entity.request.*;
import community.hospital.entity.response.LoginResultModel;
import community.hospital.entity.response.UserInfoModel;
import community.hospital.entity.response.UserResponse;
import community.hospital.service.application.UserApplicationService;
import community.hospital.service.domain.MenuService;
import community.hospital.service.domain.RoleService;
import community.hospital.service.domain.UserService;
import community.hospital.util.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static community.hospital.common.Constant.*;
import static community.hospital.common.RedisConstant.*;
import static community.hospital.enums.BusinessEnum.*;

/**
 * @Author：ydj
 * @Date：2023/1/16 16:59
 */
@Service
public class UserApplicationServiceImpl implements UserApplicationService {
    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menuService;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private RedisUtil redisUtil;

    @Override
    public boolean addUser(AddUserRequest request) {
        ListUserRequest userRequest = new ListUserRequest();
        User user = UserConvert.INSTANCE.convertToUser(request);
        isAccountExit(user);
        //生成随机盐
        String salt = RandomUtils.getSalt();
        //对密码进行加密
        String encrypt = MD5Util.encrypt(user.getPassword(), salt);
        user.setPassword(encrypt);
        user.setSalt(salt);
        return userService.save(user);

    }

    @Override
    public R<LoginResultModel> login(LoginRequest request) {
        String account = request.getAccount();
        User user = userService.findUserByAccount(account);
        if (user == null) {
            throw new CustomerException(USER_NOT_FOUND);
        }
        String salt = user.getSalt();
        String encrypt = MD5Util.encrypt(request.getPassword(), salt);
        if (!user.getPassword().equals(encrypt)) {
            throw new CustomerException(PASSWORD_ERROR);
        }
        UserResponse userResponse = UserConvert.INSTANCE.convertToResp(user);
        Role role = roleService.getById(user.getRoleId());
        userResponse.setRole(role);
        List<Menu> menus = menuService.listMenuByRoleId(user.getRoleId());
        userResponse.setMenu(menus);
        LoginUserHolder.setUser(userResponse);
        Map<String, Object> map = new HashMap<>(2);
        map.put(JWT_USER_ID, userResponse.getId());
        String token = JwtUtil.getToken(map);
        redisUtil.set(LOGIN_USER + SPLIT + user.getId(), userResponse, USER_PERSISTENCE_TIME, TimeUnit.SECONDS);

        LoginResultModel loginResultModel = new LoginResultModel()
                .setUserId(user.getId())
                .setToken(token)
                .setRoleInfo(RoleConvert.INSTANCE.convertToRoleInfo(role));
        //用于统计访问量
        threadPoolTaskExecutor.execute(() -> {
            redisUtil.increHash(PER_HOUR_VISITS, LocalDate.now()+SPLIT+LocalDateTime.now().getHour());
            redisUtil.increHash(PER_DAY_VISITS, LocalDate.now().toString());
        });
        return R.ok(loginResultModel);
    }

    @Override
    public Boolean logout(HttpServletRequest request) {
        String tokenValue = request.getHeader(TOKEN_NAME);
        Long userId = JwtUtil.getUserId(tokenValue);
        return redisUtil.del(LOGIN_USER + SPLIT + userId);
    }

    @Override
    public Page<UserResponse> listUser(ListUserRequest request) {
        Page<UserResponse> userResponsePage = userService.listUser(request);
        //获取角色id
        Set<Long> roleId = userResponsePage.getRecords().stream()
                .map(UserResponse::getRoleId).collect(Collectors.toSet());
        if (CollUtil.isEmpty(roleId)) {
            return userResponsePage;
        }
        //查询当前页的所有角色
        List<Role> roles = roleService.listByIds(roleId);
        //查询当前页的所有菜单
//        List<Menu> menus = menuService.listMenuByRoleIds(roles.stream().map(Role::getId).collect(Collectors.toList()));
//        List<Map<Long, Menu>> menus = menuService.mapMenuByRoleIds(roles.stream().map(Role::getId).collect(Collectors.toList()));

        userResponsePage.getRecords().forEach(userResponse -> {
            //查询每个用户对应的角色
            if (userResponse.getRoleId() != null) {
                userResponse.setRole(matchRole(roles, userResponse.getRoleId()));
                //查询每个用户的权限
//                List<Menu> menuList = menus.stream().filter(map -> map.get(userResponse.getRoleId()) != null)
//                        .map(map -> map.get(userResponse.getRoleId()))
//                        .collect(Collectors.toList());
                List<Menu> menuList = menuService.listMenuByRoleId(userResponse.getRoleId());
                userResponse.setMenu(menuList);
            }
        });
        return userResponsePage;
    }

    private Role matchRole(List<Role> roles, Long id) {
        for (Role role : roles) {
            if (role.getId().equals(id)) {
                return role;
            }
        }
        return null;
    }


    @Override
    public Boolean removeUser(BaseRemove request) {
        return userService.removeByIds(request.getIds());
    }

    @Override
    public Boolean updateUser(UpdateUserRequest request) {
        User user = UserConvert.INSTANCE.convertToUser(request);
        isAccountExit(user);
        return userService.updateById(user);
    }

    @Override
    public Boolean updatePassword(UpdatePasswordRequest request) {
        User user = UserConvert.INSTANCE.convertToUser(request);
        String salt = RandomUtils.getSalt();
        String encrypt = MD5Util.encrypt(user.getPassword(), salt);
        user.setPassword(encrypt);
        user.setSalt(salt);
        return userService.updateById(user);
    }

    @Override
    public UserResponse queryByUserId(Long userId) {
        User user = userService.getById(userId);
        return UserConvert.INSTANCE.convertToResp(user);
    }

    @Override
    public UserInfoModel getUserInfoModel() {
        UserResponse userResponse = LoginUserHolder.getUser();
        UserInfoModel userInfoModel = new UserInfoModel()
                .setUserId(userResponse.getId())
                .setRealName(userResponse.getUsername())
                .setUsername(userResponse.getAccount())
                .setRoles(Arrays.asList(RoleConvert.INSTANCE.convertToRoleInfo(userResponse.getRole())));

        return userInfoModel;
    }

    @Override
    public UserResponse getUserInfo(Long userId) {
        User user = userService.getById(userId);
        return UserConvert.INSTANCE.convertToResp(user);
    }

    @Override
    public Boolean isAccountExit(User user) {
        User newUser = getNewFiled(user);
        if (newUser.getAccount() != null) {
            int count = userService.countByAccount(user.getAccount());
            if (count > 0) {
                throw new CustomerException(ACCOUNT_REGISTER_REPEAT);
            }
        }
        if (newUser.getIdCard() != null) {
            int count = userService.countByIdCard(user.getIdCard());
            if (count > 0) {
                throw new CustomerException(ID_CARD_REGISTER_REPEAT);
            }
        }
        if (newUser.getPhone() != null) {
            int count = userService.countByPhone(user.getPhone());
            if (count > 0) {
                throw new CustomerException(PHONE_REGISTER_REPEAT);
            }
        }
        return true;
    }

    @Override
    public Boolean updateCurrentUserPassword(PasswordRequest request) {
        UserResponse user = LoginUserHolder.getUser();
        User dbUser = userService.getById(user.getId());
        //判断旧密码是否相同
        String oldPassword = request.getOldPassword();
        String encrypt = MD5Util.encrypt(oldPassword, dbUser.getSalt());
        if (!encrypt.equals(dbUser.getPassword())) {
            throw new CustomerException(OLD_PASSWORD_ERROR);
        }
        //更新密码
        UpdatePasswordRequest passwordRequest = new UpdatePasswordRequest(user.getId());
        passwordRequest.setPassword(request.getPassword());
        return updatePassword(passwordRequest);

    }

    @SuppressWarnings("all")
    private User getNewFiled(User user) {
        if (user.getId() == null) {
            return user;
        }
        //传入的对象属性
        Map filed = JSONObject.parseObject(JSONObject.toJSONString(user), Map.class);
        //数据库的对象属性
        Map dbFiled = new HashMap(16);
        User dbUser = userService.getById(user.getId());
        dbFiled = JSONObject.parseObject(JSONObject.toJSONString(dbUser), Map.class);

        //找出和原来属性值不同的属性
        dbFiled.forEach((k, v) -> {
            Object value = filed.get(k);
            if (value == v || (value != null && value.equals(v))) {
                filed.remove(k);
            }
        });
        return JSONObject.parseObject(JSONObject.toJSONString(filed), User.class);
    }

}
